//============================================================================
// Name        : battle_region.cpp
// Author      : dfoelber
//============================================================================

// INCLUDES //
#include "battle_region.h"
#include "render_manager.h"
#include "image_manager.h"
#include "graphics/graphics.h"
#include "physics/sin_path.h"
#include "draw/SDL_draw.h"
#include "util.h"
#include <iostream>
#include <sstream>
//////////////

using namespace std;

/**
* Constructor
**/
BattleRegion::BattleRegion()
{
	playerHealthPercent = enemyHealthPercent = playerHealth = enemyHealth = totalPlayerHealth = totalEnemyHealth = 0;
	
	Surface* healthSurface = ImageManager::GetManager()->CreateImage(0,0);
	playerHealthBar = new Object(healthSurface, OBJECT_ID_NONE, true);
	
	Surface* enemySurface = ImageManager::GetManager()->CreateImage(0,0);
	enemyHealthBar = new Object(enemySurface, OBJECT_ID_NONE, true);
	
	playerObject = new PlayerObject(ImageManager::GetManager()->GetImage(IMAGE_GOOD_WIZARD_STATIC));
	AddObject(playerObject);
	
	enemyObject = new PlayerObject(Util::Flip(ImageManager::GetManager()->GetImage(IMAGE_BAD_WIZARD_STATIC),FLIP_HORIZONTAL), true);
	AddObject(enemyObject);
}

/**
* Destructor
**/
BattleRegion::~BattleRegion()
{
	if (playerHealthBar)
	{
		delete playerHealthBar;
		playerHealthBar = NULL;
	}
	
	if (enemyHealthBar)
	{
		delete enemyHealthBar;
		enemyHealthBar = NULL;
	}
	
	for( int i = 0; i < (int)playerToCast.size(); i++ )
	{
		delete playerToCast[i]->animationObject;
		delete playerToCast[i];
	}
	
	for( int i = 0; i < (int)animatingEffects.size(); i++ )
	{
		delete animatingEffects[i]->animationObject;
		delete animatingEffects[i];
	}
	
	for( int i = 0; i < (int)animations.size(); i++ )
	{
		delete animations[i];
	}
}

/**
* Sets the region's boundary.
* 
* Overridden from Region.
* 
* @param x The boundary's X coordinate
* @param y The boundary's Y coordinate
* @param width The boundary's width
* @param height The boundary's height
**/
void BattleRegion::SetBoundary( int x, int y, int width, int height )
{
	Region::SetBoundary(x, y, width, height);
	
	Color black;
	black.r = black.g = black.b = 0;
	Surface* healthSurface = ImageManager::GetManager()->CreateImage(BAR_WIDTH, height);
	playerHealthBar->GetPath()->SetPosition(x,y);
	Draw_FillRect(healthSurface->GetSurface(), 0, 0, healthSurface->GetWidth(), healthSurface->GetHeight(), 0);
	playerHealthBar->SetSurface(healthSurface, true);
	
	Surface* enemySurface = ImageManager::GetManager()->CreateImage(BAR_WIDTH, height);
	enemyHealthBar->GetPath()->SetPosition(x + width - BAR_WIDTH,y);
	Draw_FillRect(enemySurface->GetSurface(), 0, 0, enemySurface->GetWidth(), enemySurface->GetHeight(), 0);
	enemyHealthBar->SetSurface(enemySurface, true);
	
	playerObject->SetPosition( x + BAR_WIDTH, y + height - playerObject->GetPath()->GetHeight() - 4 );
	enemyObject->SetPosition( x + width - enemyObject->GetPath()->GetWidth() - BAR_WIDTH, y + height - playerObject->GetPath()->GetHeight() - 4 );
}

/**
* Notify the region of an event.
* 
* Overridden from Region.
* 
* @param event The event that happened
* @return If the region used the event
**/
bool BattleRegion::ProcessEvent( SDL_Event event )
{
	return Region::ProcessEvent( event );
}

/**
* Notifies of time passing.
* 
* Overridden from Region.
* 
* @param The amount of time, in milliseconds, that passed since the last call
**/
void BattleRegion::TimePassed( int ms )
{
	for( vector<AnimationObject*>::iterator it = animations.begin(); it < animations.end(); it++ )
	{
		if( !(*it)->IsAnimating() )
		{
			RemoveObject(*it);
			
			it = animations.erase(it);
			
			if( it != animations.begin() ) it--;
		}
	}
	
	bool playerCasting = playerObject->IsCasting();
	Region::TimePassed(ms);
	
	for( int i = 0; i < (int)animatingEffects.size(); i++ )
	{
		if( animatingEffects[i]->GetId() == EFFECT_DAMAGE_OVER_TIME )
		{
			DoTEffect* effect = ((DoTEffect*)animatingEffects[i]);
			effect->timeCounter += ms;
			
			if( (effect->timeCounter / 1000) / effect->GetDamage()[effect->level].occur > effect->tickCounter )
			{
				effect->tickCounter++;
				int damage = effect->GetDamage()[effect->level].damage;
				// TODO: Alignment affecting damage
				enemyHealth -= damage;
			}
			
			if( effect->timeCounter >= 
				effect->GetDamage()[effect->level].time * 1000 )
			{
				effect->animationObject->StopAnimating();
			}
		}
	}
	
	if( playerCasting && !playerObject->IsCasting() )
	{
		if( (int)playerToCast.size() > 0 )
		{
			Effect* effect = playerToCast[0];
			
			playerToCast.erase( playerToCast.begin() );
			
			if( effect->GetAnimation() == EFFECT_ANIMATION_DEFAULT )
			{
				EffectsManager::GetManager()->ChangeEffectState( effect, EFFECT_STATE_ANIMATION_DONE );
			}
			else if( effect->GetAnimation() == EFFECT_ANIMATION_FIREBALL )
			{
				StartFireballAnimation(effect);
			}
			else if( effect->GetAnimation() == EFFECT_ANIMATION_BURN )
			{
				StartBurnAnimation(effect);
			}
			
			if( (int)playerToCast.size() > 0 )
			{
				playerObject->StartCastAnimation();
			}
		}
	}
	
	bool cleaning;
	do
	{
		cleaning = false;
		for( int i = 0; i < (int)animatingEffects.size(); i++ )
		{
			if( !(animatingEffects[i]->animationObject->IsAnimating()) )
			{
				EffectsManager::GetManager()->ChangeEffectState( animatingEffects[i], EFFECT_STATE_ANIMATION_DONE );
				
				RemoveObject(animatingEffects[i]->animationObject);
				animatingEffects[i]->animationObject = NULL;
				animatingEffects.erase( animatingEffects.begin() + i );
				
				cleaning = true;
				break;
			}
		}
	}
	while( cleaning );
	
	// Health Bar
	bool repaint = false;
	if( playerHealthPercent < GetPercentHealth() )
	{
		playerHealthPercent += (double)ms / 20.0;
		if( playerHealthPercent > 100 )
			playerHealthPercent = 100;
		repaint = true;
	}
	if( playerHealthPercent > GetPercentHealth() )
	{
		playerHealthPercent -= (double)ms / 20.0;
		if( playerHealthPercent < 0 )
			playerHealthPercent = 0;
		repaint = true;
	}
	
	if( repaint )
	{
		Color black;
		black.r = black.g = black.b = 0;
		SDL_Rect rect;
		rect.x = 0; rect.y = 0; rect.w = playerHealthBar->GetPath()->GetWidth(); rect.h = playerHealthBar->GetPath()->GetHeight();
		Draw_FillRect(playerHealthBar->GetSurface()->GetSurface(), 0, 0, playerHealthBar->GetPath()->GetWidth(), playerHealthBar->GetPath()->GetHeight(), 0);

		double height = playerHealthBar->GetPath()->GetHeight() * (playerHealthPercent / 100);
		ImageManager::GetManager()->GetImage(IMAGE_PLAYER_HEALTH)->Apply(playerHealthBar->GetSurface(),
				0,
				0,
				playerHealthBar->GetSurface()->GetWidth(),
				(int)height,
				0,
				playerHealthBar->GetPath()->GetHeight() - (int)height);
	}
	
	// Enemy Health Bar
	repaint = false;
	if( enemyHealthPercent < GetPercentEnemyHealth() )
	{
		enemyHealthPercent += (double)ms / 20.0;
		if( enemyHealthPercent > 100 )
			enemyHealthPercent = 100;
		repaint = true;
	}
	if( enemyHealthPercent > GetPercentEnemyHealth() )
	{
		enemyHealthPercent -= (double)ms / 20.0;
		if( enemyHealthPercent < 0 )
			enemyHealthPercent = 0;
		repaint = true;
	}
	
	if( repaint )
	{
		Color black;
		black.r = black.g = black.b = 0;
		SDL_Rect rect;
		rect.x = 0; rect.y = 0; rect.w = enemyHealthBar->GetPath()->GetWidth(); rect.h = enemyHealthBar->GetPath()->GetHeight();
		Draw_FillRect(enemyHealthBar->GetSurface()->GetSurface(), 0, 0, enemyHealthBar->GetPath()->GetWidth(), enemyHealthBar->GetPath()->GetHeight(), 0);

		double height = enemyHealthBar->GetPath()->GetHeight() * (enemyHealthPercent / 100);
		ImageManager::GetManager()->GetImage(IMAGE_PLAYER_HEALTH)->Apply(enemyHealthBar->GetSurface(),
				0,
				0,
				enemyHealthBar->GetSurface()->GetWidth(),
				(int)height,
				0,
				enemyHealthBar->GetPath()->GetHeight() - (int)height);
	}
}

/**
* Renders the objects on the background
* 
* Overridden from Region.
**/
void BattleRegion::Render()
{
}

/**
* An effect's state has changed.
* 
* Overridden from EffectsListener
* 
* @param effect The effect that is updated
**/
void BattleRegion::EffectStateChanged( Effect* effect )
{
	if( effect->state == EFFECT_STATE_READY && effect->GetAnimation() == EFFECT_ANIMATION_NONE )
	{
		EffectsManager::GetManager()->ChangeEffectState( effect, EFFECT_STATE_ANIMATION_DONE );
		return;
	}
	
	if( effect->state == EFFECT_STATE_READY && effect->useCastAnimation )
	{
		bool contains = false;
		for( int i = 0; i < (int)playerToCast.size(); i++ )
		{
			if( effect->uid == playerToCast[i]->uid )
			{
				contains = true;
				break;
			}
		}
		
		if( !contains )
			playerToCast.push_back( effect );
		
		if( !playerObject->IsCasting() )
		{
			playerObject->StartCastAnimation();
			
			// for testing - build up
			AnimationObject* animation = new AnimationObject();
			AddObject(animation);
			animation->SetAnimationImages(ImageManager::GetManager()->GetRedBuildupAnimation(), true, AnimationObject::DURATION_ONCE);
			animation->SetPosition(playerObject->GetPath()->GetX(),playerObject->GetPath()->GetY());
			animations.push_back(animation);
		}
	}
	else if( effect->state == EFFECT_STATE_READY )
	{
		if( effect->GetAnimation() == EFFECT_ANIMATION_DEFAULT )
		{
			EffectsManager::GetManager()->ChangeEffectState( effect, EFFECT_STATE_ANIMATION_DONE );
			delete effect;
		}
		else if( effect->GetAnimation() == EFFECT_ANIMATION_FIREBALL )
		{
			StartFireballAnimation(effect);
		}
		else if( effect->GetAnimation() == EFFECT_ANIMATION_BURN )
		{
			StartBurnAnimation(effect);
		}
	}
	else if( effect->GetId() == EFFECT_DIRECT_DAMAGE && effect->GetTrigger() == effect->state )
	{
		int damage = ((DirectDamageEffect*)effect)->GetDamage()[effect->level];
		// TODO: Alignment affecting damage
		enemyHealth -= damage;
	}
}

/*
* Calculates the player's health as a percentage.
* 
* @return The player's health.
**/
int BattleRegion::GetPercentHealth()
{
	if( totalPlayerHealth == 0 )
		return 0;
	
	else return (int)(( (double)playerHealth / (double)totalPlayerHealth ) * 100);
}

/*
* Calculates the enemy's health as a percentage.
* 
* @return The enemy's health.
**/
int BattleRegion::GetPercentEnemyHealth()
{
	if( totalEnemyHealth == 0 )
		return 0;
	
	else return (int)(( (double)enemyHealth / (double)totalEnemyHealth ) * 100);
}

/**
* Gets the origin coordinates.
* 
* @param effect The effect to extract the origin.
* @param x The origin's X coordinate.
* @param y The origin's y coordinate.
**/
void BattleRegion::GetOriginCoords(Effect* effect, int* x, int* y)
{
	*x = *y = 0;
	switch( effect->origin )
	{
	case EFFECT_ORIGIN_PLAYER:
		*x = playerObject->GetPath()->GetX() + playerObject->GetPath()->GetWidth();
		*y = playerObject->GetPath()->GetY();
		break;
	default:
		break;
	}
}

/**
* Gets the target coordinates.
* 
* @param effect The effect to extract the target.
* @param A list of target points
**/
vector<Point> BattleRegion::GetTargetCoords(Effect* effect)
{
	vector<Point> points;
	
	if( effect->GetTarget().find(EFFECT_TARGET_ENEMY) != string::npos )
	{
		Point point;
		point.x = enemyObject->GetPath()->GetX();
		point.y = enemyObject->GetPath()->GetY();
		points.push_back(point);
	}
	
	return points;
}

/**
* Starts a fireball animation.
* 
* @param effect The effect for the fireball animation.
**/
void BattleRegion::StartFireballAnimation(Effect* effect)
{
	EffectsManager::GetManager()->ChangeEffectState( effect, EFFECT_STATE_ANIMATION_STARTED );
	int originX = 0, originY = 0, targetX = 0, targetY = 0;
	GetOriginCoords( effect, &originX, &originY );
	vector<Point> targets = GetTargetCoords( effect );
	
	// for now, handle just one target
	if( (int)targets.size() > 0 )
	{
		targetX = targets[0].x; targetY = targets[0].y;
		AnimationObject* animation = new AnimationObject();
		SinPath* path = new SinPath(originX, originY, targetX, targetY, (targetX - originX) / 2, originY - 40 < 0 ? 0 : originY - 40, 15);
		animation->SetPath(path);
		AddObject(animation);
		vector<Surface*> fireball;
		fireball.push_back(ImageManager::GetManager()->GetImage(IMAGE_FIREBALL_ANIM_1));
		fireball.push_back(ImageManager::GetManager()->GetImage(IMAGE_FIREBALL_ANIM_2));
		fireball.push_back(ImageManager::GetManager()->GetImage(IMAGE_FIREBALL_ANIM_3));
		fireball.push_back(ImageManager::GetManager()->GetImage(IMAGE_FIREBALL_ANIM_4));
		fireball.push_back(ImageManager::GetManager()->GetImage(IMAGE_FIREBALL_ANIM_5));
		fireball.push_back(ImageManager::GetManager()->GetImage(IMAGE_FIREBALL_ANIM_6));
		animation->SetAnimationImages(fireball, true, AnimationObject::DURATION_PATH);
		animation->SetPosition(originX,originY);
		effect->animationObject = animation;
		animatingEffects.push_back(effect);
	}
	else
	{
		// abort...
	}
}

/**
* Starts a burn animation.
* 
* @param effect The effect for the burn animation.
**/
void BattleRegion::StartBurnAnimation(Effect* effect)
{
	EffectsManager::GetManager()->ChangeEffectState( effect, EFFECT_STATE_ANIMATION_STARTED );
	vector<Point> targets = GetTargetCoords( effect );
	
	// For now, just one target
	if( (int)targets.size() > 0 )
	{
		AnimationObject* animation = new AnimationObject();
		AddObject(animation);
		vector<Surface*> burn;
		burn.push_back(ImageManager::GetManager()->GetImage(IMAGE_BURN_ANIM_1));
		burn.push_back(ImageManager::GetManager()->GetImage(IMAGE_BURN_ANIM_2));
		burn.push_back(ImageManager::GetManager()->GetImage(IMAGE_BURN_ANIM_3));
		burn.push_back(ImageManager::GetManager()->GetImage(IMAGE_BURN_ANIM_4));
		burn.push_back(ImageManager::GetManager()->GetImage(IMAGE_BURN_ANIM_5));
		burn.push_back(ImageManager::GetManager()->GetImage(IMAGE_BURN_ANIM_6));
		burn.push_back(ImageManager::GetManager()->GetImage(IMAGE_BURN_ANIM_7));
		burn.push_back(ImageManager::GetManager()->GetImage(IMAGE_BURN_ANIM_8));
		burn.push_back(ImageManager::GetManager()->GetImage(IMAGE_BURN_ANIM_9));
		burn.push_back(ImageManager::GetManager()->GetImage(IMAGE_BURN_ANIM_10));
		animation->SetAnimationImages(burn, false, AnimationObject::DURATION_EXTERNAL);
		animation->SetPosition(targets[0].x,targets[0].y);
		effect->animationObject = animation;
		animatingEffects.push_back(effect);
	}
	else
	{
		// abort...
	}
}
